TanStack Query 请求库
TanStack Query 是什么
tanstack/react-query 库是一个用于在 React 应用中进行数据获取、缓存、同步和更新的强大工具。
它提供了一种简单而高效的方式来从远程源获取数据,并将这些数据以钩子的形式直接同步到你的组件中。React Query 自带数据缓存和无效化机制,大大减少了手动管理服务器状态和UI状态之间复杂性的需要。
它的主要特点包括:
- 自动数据缓存:对请求的数据进行智能缓存,减少不必要的数据重新获取。
- 后台数据更新:在后台更新缓存数据,同时保持界面响应。
- 数据同步:自动同步后台数据到 UI,保持数据一致性。
- 查询失效化:提供机制让缓存数据在适当的时候失效,以获取最新数据。
- 轮询、预加载、分页和无限滚动:内置支持多种数据获取模式和策略。
npm install @tanstack/react-query
然后,在组件中使用 useQuery 钩子获取数据:
import React from 'react';
import { useQuery } from '@tanstack/react-query';
async function fetchPosts() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
}
function Posts() {
const { isLoading, error, data } = useQuery(['posts'], fetchPosts);
if (isLoading) return 'Loading...';
if (error) return 'An error has occurred: ' + error.message;
return (
<div>
<h1>Posts</h1>
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default Posts;
在这个例子中,我们创建了一个 fetchPosts 函数来获取博客帖子的数据,然后在 Posts 组件中通过 useQuery 钩子来调用这个函数。useQuery 接受两个参数:一个唯一标识当前查询的键(在这个例子中是 ['posts'])和一个返回 Promise 的函数(fetchPosts)。
useQuery 钩子返回一个对象,其中包含状态信息如 isLoading、error 和 data,我们可以用这些状态来在组件中适当地渲染加载状态、错误信息或者实际的数据。
查询键(Query Key)的作用:
在上面提供的 React Query 使用示例中,posts 是传递给 useQuery 钩子的查询键(query key)。在 React Query 中,查询键是一个独特的标识符,用于追踪和缓存特定数据获取操作的结果。
- 标识和缓存数据:React Query 使用查询键来缓存加载的数据。当相同的查询键再次用于
useQuery时,React Query 会首先尝试从缓存中返回数据,而不是重新发起请求,从而提高效率和性能。 - 查询失效化和更新:通过查询键,React Query 可以轻松地让特定的数据失效并重新获取,这在数据变更后需要更新UI显示最新数据时非常有用。
- 并行和依赖查询:查询键还允许开发者实现更复杂的数据获取模式,如并行查询、依赖查询等,通过对查询键的控制来精确管理不同数据间的依赖和更新时机。
在上述示例中,['posts'] 作为查询键,标识了一个用于获取帖子列表的数据查询。这个键是唯一的,对应于调用 fetchPosts 函数的数据获取操作。如果你在应用中的另一个组件中也使用了相同的查询键 ['posts'] 来获取帖子,React Query 将会复用缓存中的数据(如果可用),而不是重新发起网络请求。
动态更新页面
下面是一个更复杂的示例,展示了如何使用 React Query 来获取平台列表数据,并根据平台类型分组渲染到页面上。
import React from 'react';
import { Tabs, TabList, Tab, TabPanels, TabPanel, Box, Text } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { getPlatformList } from '../../services/platform/controller';
import PageContainer from '../../components/PageContainer';
const HomePage: React.FC = () => {
const { isLoading, data } = useQuery(['platformList'], getPlatformList);
// 根据平台类型分组平台数据
const groupedPlatforms = data?.reduce((acc, platform) => {
const type = platform.type || 'other'; // 将未指定类型的平台归类到'other'
if (!acc[type]) acc[type] = [];
acc[type].push(platform);
return acc;
}, {} as Record<string, Platform[]>);
// 根据类型渲染TabPanel内容
const renderTabPanels = () => {
const types = Object.keys(groupedPlatforms || {});
return types.map(type => (
<TabPanel key={type}>
{groupedPlatforms?.[type].map(platform => (
<Box key={platform.id} p={2}>
<Text fontSize="lg">{platform.name}</Text>
</Box>
))}
</TabPanel>
));
};
if (isLoading) return <PageContainer>Loading...</PageContainer>;
return (
<PageContainer>
<Tabs variant="enclosed">
<TabList>
{Object.keys(groupedPlatforms || {}).map(type => (
<Tab key={type}>{type}</Tab>
))}
</TabList>
<TabPanels>
{renderTabPanels()}
</TabPanels>
</Tabs>
</PageContainer>
);
};
export default HomePage;